《ECMAScript 6入门》阅读笔记(下)
下半部分,主要考量了全新的异步模式 Promise 和 Generator 以及它们的语法糖 async。当然,还有超级好用的的模块实现,以及让人头大的的类,这里建议配合《你不知道的JavaScript(中)》的第二部分反复阅读,理解 JavaScript 强大的异步实现

Promise 对象
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大
- 两个特点
- Promise 对象有三种状态:Pending、Resolved 和 Rejected。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
- 无法取消 Promise,一旦新建它就会立即执行,无法中途取消
- 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部
- 处于 Pending 状态时,无法得知目前进展到哪一个阶段
|
|
resolve 的参数可以也可是一个 promise 实例
1234567var p1 = new Promise(function (resolve, reject) {// ...})var p2 = new Promise(function (resolve, reject) {// ...resolve(p1) // 在这里,p1 的状态就传给了 p2,会等待 p1 的状态决议后才执行})Promise.prototype.then()本方法接受两个参数
resolve和reject(但不建议使用第二个参数,而是使用.catch()方法替代),返回一个新的 Promise 实例(不是原来那个),因此可以使用链式继续调用.then()方法
|
|
Promise.all()方法用于多个 promise 实例- 全部 promise 都变成 resolve,才返回 resolve
- 只要有一个 reject,返回第一个 reject 那个
Promise.race()返回最先被决议的那个 promisePromise.resolve()方法会将一个普通对象转换成 Promise 对象123Promise.resolve('foo')// 等价于new Promise(resolve => resolve('foo'))
Iterator 和 for…of 循环
Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费
|
|
- 在ES6中,有三类数据结构原生具备 Iterator 接口:数组、某些类似数组的对象、Set 和 Map 结构
Generator
用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的
next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束
- Generator 函数本质上还是函数,但这个函数可以分段执行返回
- Generator 函数可以不用
yield语句,这时就变成了一个单纯的暂缓执行函数。但yield语句只能用在 Generator 函数中 - Generator 函数是 ES6 新增的底层实现,跟 Promise 不同
使普通对象具备迭代器接口
12345678var myIterable = {};myIterable[Symbol.iterator] = function* () {yield 1;yield 2;yield 3;};//[...myIterable] // [1, 2, 3]yield句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值1234567891011function* f() {for(var i = 0; true; i++) {var reset = yield i;if(reset) { i = -1; }}}// 可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为var g = f();g.next() // { value: 0, done: false }g.next() // { value: 1, done: false }g.next(true) // { value: 0, done: false }for...of循环可以自动遍历 Generator 函数时生成的 Iterator 对象,且此时不再需要调用next方法12345678910111213function *foo() {yield 1;yield 2;yield 3;yield 4;yield 5;return 6;}for (let v of foo()) {console.log(v);}// 1 2 3 4 5 不包含返回对象配合
try...catch和Generator.throw()方法,可以方便地根据需要在函数内外捕获错误Generator.return()方法可以返回值并停止迭代yield*后面的 Generator 函数(没有 return 语句时),不过是for...of的一种简写形式,完全可以用后者替代前者。反之,则需要用var value = yield* iterator的形式获取return语句的值- 任何数据结构只要有 Iterator 接口,就可以被
yield*遍历 - 其返回的 Promise 对象的只在所有异步状态都完成后才决议(除非提前 return 或抛出错误),非常类似 Promise.all
- 使用 Generator 包装一个对象12345678910function* objectEntries(obj) {let propKeys = Reflect.ownKeys(obj)for (let propKey of propKeys) {yield [propKey, obj[propKey]]}}let jane = { first: 'Jane', last: 'Doe' }for (let [key, value] of objectEntries(jane)) {console.log(`${key}: ${value}`)}
async 函数(ES2017)
是 Generator 函数的语法糖
- 自带执行器,因此不用手动调用
next(),可以看作是多个异步操作包装成的 Promise 对象,因此下一步通常是调用then方法进行处理,本质上等于把 Generator 和它的执行期封装在一个函数中 - 具备五种定义方法
- 函数声明
- 函数表达式
- 对象方法
- 类
- 箭头函数
await命令后面一定是一个 Promise 对象(自动包装)- 为了能方便捕获错误,最好讲
await操作放在try...catch中1234567async function myFunction() {try {await somethingThatReturnsAPromise()} catch (err) {console.log(err)}}
Class
基本应用
1234567891011121314151617181920// ES5function Person(name, age) {this.name = namethis.age = age}Person.prototype.getName = function () {return this.name}// ES6class Person {constructor (name, age) {this.name = namethis.age = age}//getName () {return this.name}}Class必须用new调用,否则报错,同时不存在变量提升- 与ES5不同的地方是,
Class定义的方法是不可枚举的 constructor方法是类的默认方法,如果不指定,则默认添加一个空方法Class中默认就是严格模式- 类之间通过
extends关键字实现继承1234567891011121314// ColorPoint 类,该类通过 extends 关键字,继承了 Point 类的所有属性和方法class ColorPoint extends Point {} // 这里相当于复制//class ColorPoint extends Point {// 子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工constructor(x, y, color) {super(x, y); // 调用父类的 constructor(x, y)this.color = color;}toString() {return this.color + ' ' + super.toString(); // 调用父类的 toString()}}
Module
- CommonJS 与 ES6 的区别
- CommonJS 先生成对象再读取其中的方法,运行时加载,无法静态优化;ES6 模块不是对象,编译时加载,可实现静态优化
- CommonJS 输出的是值的缓存,不存在动态更新;ES6 模块输出的是值的引用
- ES6 中的模块自动采用严格模式
export命令,输出模块的变量(否则外部无法读取),该命令不能出现在块作用域中import命令加载模块并读取其中变量,该命令有提升作用,自动提升到整个模块头部优先执行,该命令不能出现在块作用域中import是静态执行的,所以不能使用表达式和变量这些运行时语法,该命令是 Singleton 模式- 使用默认输出时,
import语句无需大括号 - 浏览器中使用模块,该模块默认为延迟脚本1<script type="module" src="foo.js"></script>